package com.fsm.utils;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import redis.clients.jedis.Jedis;
/*******************
 *@Describe 错误锁方式B
 *
 *@author 封神梦
 *@Time 2023/6/13
 *@Company http://com.yiview.com
 ****************/

public class WrongBRedisDLock   {
    private static final Logger LOG = LogManager.getLogger(WrongBRedisDLock.class);
    private final String lockName;
    private final long lockDuration;
    private final String lockSubject;
    private final Jedis jedis;
    private boolean isLocked = false;

    public WrongBRedisDLock(String lockName, long lockDuration, String lockSubject, Jedis jedis) {
        this.lockName = lockName;
        this.lockDuration = lockDuration;
        this.lockSubject = lockSubject;
        this.jedis = jedis;
    }
    public void lock() {
        tryLock(Long.MAX_VALUE);
    }
    public boolean tryLock() {
        return tryLock(0);
    }
    public boolean tryLock(long waitTimeout) {
        long loopStartTime = System.currentTimeMillis();
        while (true) {
            try {
                long startTime = System.currentTimeMillis();
                String expireTimeStr = String.valueOf(startTime + lockDuration + 1);
                // 设置锁名和锁的到期时间，此处就存在多个不同进程或服务器当前时间可能会不一致的问题
                if (jedis.setnx(lockName, expireTimeStr) != null) {
                    isLocked = true;
                    return true;
                }
                // 获取锁的过期时间
                String oldExpireTimeStr = jedis.get(lockName);
                if (oldExpireTimeStr == null) {
                    continue;
                }
                long oldExpireTime = Long.parseLong(oldExpireTimeStr);
                if (startTime > oldExpireTime) {
                    // 在并发量较大时，可能会有多个锁申请主体同时进入到这里，并且都会修改锁的过期时间，
                    // 这样会造成锁的实际过期时间比锁执有者设置的时间靠后
                    oldExpireTimeStr = jedis.getSet(lockName, expireTimeStr);
                    if (oldExpireTimeStr != null) {
                        // 以下代码可以保证第一个申请成功的主体获取到锁
                        oldExpireTime = Long.parseLong(oldExpireTimeStr);
                        if (startTime > oldExpireTime) {
                            isLocked = true;
                            return true;
                        }
                    }
                }
                if (System.currentTimeMillis() - loopStartTime < waitTimeout) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ie) {
                    }
                    continue;
                }
                return false;
            } catch (Exception e) {
                LOG.error("tryLock error", e);
                if (System.currentTimeMillis() - loopStartTime < waitTimeout) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ie) {
                    }
                    continue;
                }
                return false;
            }
        }
    }
    public void unlock() {
        try {
            // 获取锁的到期时间
            String oldExpireTimeStr = jedis.get(lockName);
            if (oldExpireTimeStr != null) {
                long oldExpireTime = Long.parseLong(oldExpireTimeStr);
                if (oldExpireTime < System.currentTimeMillis()) {
                    // 若此时锁过期了并且被其它申请者成功获取，则将误删锁
                    jedis.del(lockName);
                } else {
                    if (isLocked) {
                        // 若此时锁过期了并且被其它申请者成功获取，则将误删锁
                        jedis.del(lockName);
                    }
                }
            }
            isLocked = false;
        } catch (Exception e) {
            LOG.error("unlock error", e);
        }
    }
}

